package com.mbc.hr.manager;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.persistence.NoResultException;
import javax.persistence.TemporalType;

import com.mbc.common.entities.SystemProperty;
import com.mbc.common.exception.DataNotFoundException;
import com.mbc.common.manager.AbstractMgr;
import com.mbc.common.utils.DateUtils;
import com.mbc.common.utils.KeyGenerator;
import com.mbc.hr.data.DailyAttendanceDataList;
import com.mbc.hr.data.HRContants;
import com.mbc.hr.entities.AttendanceLog;
import com.mbc.hr.entities.AttendancePenaltyDetail;
import com.mbc.hr.entities.AttendancePolicyDetail;
import com.mbc.hr.entities.DailyAttendance;
import com.mbc.hr.entities.DailyAttendanceLog;
import com.mbc.hr.entities.EarlyDeparturePenaltyDetail;
import com.mbc.hr.entities.Employee;
import com.mbc.hr.entities.EmployeeAttendancePenalty;
import com.mbc.hr.entities.EmployeeGroup;
import com.mbc.hr.entities.Overtime;
import com.mbc.hr.param.DailyAttendanceCriteria;

public class AttendanceMgr extends AbstractMgr {
	public AttendanceMgr(){}
	
	public AttendanceMgr(String sessionId){
		this.sessionId = sessionId;
	}
	
	private AttendancePolicyDetail getAttendancePolicyDetailFromList(List<AttendancePolicyDetail> details, int day){
		for (AttendancePolicyDetail l_detail : details) {
			if(l_detail.getDay() == day){
				return l_detail;
			}
		}
		return null;
	}
	
	private boolean isInTimeRange(int advancedPeriod, int startTime, Date inDateTime){
		Calendar l_inCalendar = Calendar.getInstance();
		l_inCalendar.setTime(inDateTime);		
		int l_earliestStartTime = startTime - advancedPeriod;
		int l_actualStartTime = (l_inCalendar.get(Calendar.HOUR_OF_DAY) * 60) + l_inCalendar.get(Calendar.MINUTE);
		return (l_actualStartTime >= l_earliestStartTime && l_actualStartTime <= startTime);
	}
	
	/***
	 * Create attendances from logs with status.
	 * @param logs
	 * @throws Exception
	 */
	private void createAttendnaceFromLog_1(List<AttendanceLog> logs)throws Exception{
		SystemProperty l_systemProperty = entityManager.find(SystemProperty.class, HRContants.AttendanceCalculationType.SYSTEM_PROPERTY_NAME);
		if(l_systemProperty != null){
			for (AttendanceLog l_log : logs) {
				Employee l_emp = null;
				try {
					l_emp = entityManager.createNamedQuery("Employee.findByFingerPrintID", Employee.class)
					.setParameter("fingerPrintID", l_log.getUserId())
					.getSingleResult();
				} catch (NoResultException e) {}
				if(l_emp != null){
					l_log.setPunchDateTime(DateUtils.convert(l_log.getPunchDateTimeStr(), "yyyyMMddHHmmss"));
					l_log.setEmployeeNo(l_emp.getEmployeeNo());
					SystemProperty l_advancedMinutes = entityManager.find(SystemProperty.class, HRContants.AttendanceCalculationType.SYSTEM_PROPERTY_ADVANCED_MIN);
					int l_advancedMin = 0;
					if(l_advancedMinutes != null)
						l_advancedMin = Integer.parseInt(l_advancedMinutes.getValue());
					EmployeeGroup l_empGroup = entityManager.find(EmployeeGroup.class, l_emp.getEmployeeGroupID());
					List<AttendancePolicyDetail> l_attPolicyDetails = entityManager.createNamedQuery("AttendancePolicyDetail.findByPolicyID", AttendancePolicyDetail.class)
							.setParameter("policyID", l_empGroup.getAttendancePolicyID())
							.getResultList();
					Calendar l_calendar = Calendar.getInstance();
					
					if(l_log.getStatus() == HRContants.AttendanceLogStatus.IN){
						l_calendar.setTime(l_log.getPunchDateTime());
						AttendancePolicyDetail l_attPolicyDetail = getAttendancePolicyDetailFromList(l_attPolicyDetails, l_calendar.get(Calendar.DAY_OF_WEEK));
						
						DailyAttendance l_dailyAttendance = null;
						try {
							Date[] l_range = DateUtils.getRangeForDate(l_log.getPunchDateTime());
							l_dailyAttendance = entityManager.createNamedQuery("DailyAttendance.getAttendanceByInDate", DailyAttendance.class)
									.setParameter("startDate", l_range[0])
									.setParameter("endDate", l_range[1])
									.setParameter("employeeId", l_emp.getEmployeeID())
									.getSingleResult();	
						} catch (NoResultException innerEx) {}
						
						if(l_attPolicyDetail != null){
							if(isInTimeRange(l_advancedMin, l_attPolicyDetail.getStartTime(), l_log.getPunchDateTime())
									&& l_dailyAttendance == null){
								int l_actualInMinutes = (l_calendar.get(Calendar.HOUR_OF_DAY) * 60) + l_calendar.get(Calendar.MINUTE);
								
								l_dailyAttendance = new DailyAttendance();
								l_dailyAttendance.setActualInDateTime(l_log.getPunchDateTime());
								l_dailyAttendance.setBranchId(l_emp.getBranchId());
								l_dailyAttendance.setCreatedDate(new Date());
								l_dailyAttendance.setEmployeeId(l_emp.getEmployeeID());
								l_dailyAttendance.setId(KeyGenerator.generateKey(entityManager));
								l_dailyAttendance.setAttendanceType(HRContants.DailyAttendanceType.NORMAL_ATTENDANCE);
								int l_startHour = l_attPolicyDetail.getStartTime() / 60;
								int l_startMin = l_attPolicyDetail.getStartTime() % 60;
								
								l_calendar.set(Calendar.HOUR_OF_DAY, l_startHour);
								l_calendar.set(Calendar.MINUTE, l_startMin);
								l_dailyAttendance.setInDateTime(new Date(l_calendar.getTime().getTime()));
								
								l_calendar.add(Calendar.MINUTE, l_attPolicyDetail.getWorkingHour());
								l_dailyAttendance.setOutDateTime(l_calendar.getTime());
								boolean l_wholeDayAbsence = false;
								if(l_dailyAttendance.getActualInDateTime().after(l_dailyAttendance.getInDateTime())){
									int l_lateMinutes = l_actualInMinutes - l_attPolicyDetail.getStartTime();
									List<AttendancePenaltyDetail> l_attPenaltyList = entityManager.createNamedQuery("AttendancePenaltyDetail.getPenaltyDetailsByEmployeeID", AttendancePenaltyDetail.class)
											.setParameter("employeeID", l_dailyAttendance.getEmployeeId())
											.getResultList();
									
									if(l_attPenaltyList.size() > 0){
										AttendancePenaltyDetail l_attPenaltyDetail = null;
										if(l_lateMinutes > l_attPenaltyList.get(l_attPenaltyList.size() - 1).getToLateTime()){
											l_attPenaltyDetail = l_attPenaltyList.get(l_attPenaltyList.size() - 1);
										}else{
											for (int i = 1; i < l_attPenaltyList.size(); i++) {
												if(l_lateMinutes >= l_attPenaltyList.get(i).getFromLateTime() && 
														l_lateMinutes <= l_attPenaltyList.get(i).getToLateTime()){
													l_attPenaltyDetail = l_attPenaltyList.get(i);
													break;
												}
											}
										}
										if(l_attPenaltyDetail != null){
											l_wholeDayAbsence = (l_attPenaltyDetail.getAbsenceType() == 1);
											
											EmployeeAttendancePenalty l_empAttPenalty = new EmployeeAttendancePenalty();
											l_empAttPenalty.setDatetime_(l_dailyAttendance.getActualInDateTime());
											l_empAttPenalty.setAbsentPeriod(l_attPenaltyDetail.getAbsenceType());
											l_empAttPenalty.setCreatedDate(new Date());
											l_empAttPenalty.setEmployeeID(l_dailyAttendance.getEmployeeId());
											l_empAttPenalty.setPenaltyType(HRContants.AttendancePenaltyType.LATE_ARRIVAL);
											l_empAttPenalty.setMinutes(l_lateMinutes);
											l_empAttPenalty.setStatus(HRContants.AttendancePenaltyStatus.ACTIVE);
											entityManager.persist(l_empAttPenalty);
										}
									}
								}
								if(!l_wholeDayAbsence)
									entityManager.persist(l_dailyAttendance);
							}
						}
						else{
							Overtime l_overtime = null;
							try {
								Date[] l_range = DateUtils.getRangeForDate(l_log.getPunchDateTime());
								l_overtime = entityManager.createNamedQuery("Overtime.findByDateNEmployeeId", Overtime.class)
									.setParameter("startDate", l_range[0])
									.setParameter("endDate", l_range[1])
									.setParameter("employeeId", l_emp.getEmployeeID())
									.getSingleResult();
							} catch (NoResultException e) {}
							if(l_overtime != null && l_dailyAttendance == null){
								Calendar l_otCalendar = Calendar.getInstance();
								l_otCalendar.setTime(l_overtime.getOtDate());
								
								l_dailyAttendance = new DailyAttendance();
								l_dailyAttendance.setActualInDateTime(l_log.getPunchDateTime());
								l_dailyAttendance.setBranchId(l_emp.getBranchId());
								l_dailyAttendance.setCreatedDate(new Date());
								l_dailyAttendance.setEmployeeId(l_emp.getEmployeeID());
								l_dailyAttendance.setId(KeyGenerator.generateKey(entityManager));
								l_dailyAttendance.setInDateTime(l_overtime.getOtDate());
								l_dailyAttendance.setAttendanceType(HRContants.DailyAttendanceType.OVERTIME);
								l_otCalendar.add(Calendar.MINUTE, l_overtime.getOtMinutes());
								l_dailyAttendance.setOutDateTime(l_otCalendar.getTime());
								
								entityManager.persist(l_dailyAttendance);
							}
						}
					}else{
						DailyAttendance l_previousAttendance = null;
						try {
							l_previousAttendance = entityManager.createNamedQuery("DailyAttendance.getPreviousAttendance", DailyAttendance.class)
									.setParameter("inDateTime", l_log.getPunchDateTime())
									.setParameter("employeeId", l_emp.getEmployeeID())
									.setFirstResult(0)
									.setMaxResults(1)
									.getSingleResult();
						} catch (NoResultException innerEx) {}
						if(l_previousAttendance != null){
							if(l_previousAttendance.getActualOutDateTime() == null){
								l_previousAttendance.setActualOutDateTime(l_log.getPunchDateTime());
								entityManager.merge(l_previousAttendance);
							}else{
								if(l_previousAttendance.getActualOutDateTime().before(l_log.getPunchDateTime())){
									l_previousAttendance.setActualOutDateTime(l_log.getPunchDateTime());
									entityManager.merge(l_previousAttendance);
								}
							}
						}
					}
					l_log.setEmployeeNo(l_emp.getEmployeeNo());
					l_log.setCreatedDate(new Date());
					entityManager.persist(l_log);
				}
			}
		}
	}
	
	/***
	 * Create daily attendances from log with no status.
	 * @param logs
	 * @throws Exception
	 */
	private void createAttendanceFromLog_3(List<AttendanceLog> logs)throws Exception{
		SystemProperty l_systemProperty = entityManager.find(SystemProperty.class, HRContants.AttendanceCalculationType.SYSTEM_PROPERTY_NAME);
		if(l_systemProperty != null){
			for (AttendanceLog l_log : logs) {
				Employee l_emp = null;
				try {
					l_emp = entityManager.createNamedQuery("Employee.findByFingerPrintID", Employee.class)
					.setParameter("fingerPrintID", l_log.getUserId())
					.getSingleResult();
				} catch (NoResultException e) {}
				if(l_emp != null){
					l_log.setPunchDateTime(DateUtils.convert(l_log.getPunchDateTimeStr(), "yyyyMMddHHmmss"));
					
					SystemProperty l_advancedMinutes = entityManager.find(SystemProperty.class, HRContants.AttendanceCalculationType.SYSTEM_PROPERTY_ADVANCED_MIN);
					int l_advancedMin = 0;
					if(l_advancedMinutes != null)
						l_advancedMin = Integer.parseInt(l_advancedMinutes.getValue());
					EmployeeGroup l_empGroup = entityManager.find(EmployeeGroup.class, l_emp.getEmployeeGroupID());
					List<AttendancePolicyDetail> l_attPolicyDetails = entityManager.createNamedQuery("AttendancePolicyDetail.findByPolicyID", AttendancePolicyDetail.class)
							.setParameter("policyID", l_empGroup.getAttendancePolicyID())
							.getResultList();
					Calendar l_calendar = Calendar.getInstance();
					
					l_calendar.setTime(l_log.getPunchDateTime());
					AttendancePolicyDetail l_attPolicyDetail = getAttendancePolicyDetailFromList(l_attPolicyDetails, l_calendar.get(Calendar.DAY_OF_WEEK));
					
					DailyAttendance l_dailyAttendance = null;
					try {
						Date[] l_range = DateUtils.getRangeForDate(l_log.getPunchDateTime());
						l_dailyAttendance = entityManager.createNamedQuery("DailyAttendance.getAttendanceByInDate", DailyAttendance.class)
								.setParameter("startDate", l_range[0])
								.setParameter("endDate", l_range[1])
								.setParameter("employeeId", l_emp.getEmployeeID())
								.getSingleResult();
					} catch (NoResultException innerEx) {}
					
					if(l_attPolicyDetail != null){
						if(isInTimeRange(l_advancedMin, l_attPolicyDetail.getStartTime(), l_log.getPunchDateTime())
								&& l_dailyAttendance == null){
							int l_actualInMinutes = (l_calendar.get(Calendar.HOUR_OF_DAY) * 60) + l_calendar.get(Calendar.MINUTE);
							
							l_dailyAttendance = new DailyAttendance();
							l_dailyAttendance.setActualInDateTime(l_log.getPunchDateTime());
							l_dailyAttendance.setBranchId(l_emp.getBranchId());
							l_dailyAttendance.setCreatedDate(new Date());
							l_dailyAttendance.setEmployeeId(l_emp.getEmployeeID());
							l_dailyAttendance.setId(KeyGenerator.generateKey(entityManager));
							l_dailyAttendance.setAttendanceType(HRContants.DailyAttendanceType.NORMAL_ATTENDANCE);
							
							int l_startHour = l_attPolicyDetail.getStartTime() / 60;
							int l_startMin = l_attPolicyDetail.getStartTime() % 60;
							
							l_calendar.set(Calendar.HOUR_OF_DAY, l_startHour);
							l_calendar.set(Calendar.MINUTE, l_startMin);
							l_dailyAttendance.setInDateTime(new Date(l_calendar.getTime().getTime()));
							
							l_calendar.add(Calendar.MINUTE, l_attPolicyDetail.getWorkingHour());
							l_dailyAttendance.setOutDateTime(l_calendar.getTime());
							
							boolean l_wholeDayAbsence = false;
							if(l_dailyAttendance.getActualInDateTime().after(l_dailyAttendance.getInDateTime())){
								int l_lateMinutes = l_actualInMinutes - l_attPolicyDetail.getStartTime();
								List<AttendancePenaltyDetail> l_attPenaltyList = entityManager.createNamedQuery("AttendancePenaltyDetail.getPenaltyDetailsByEmployeeID", AttendancePenaltyDetail.class)
										.setParameter("employeeID", l_dailyAttendance.getEmployeeId())
										.getResultList();
								
								if(l_attPenaltyList.size() > 0){
									AttendancePenaltyDetail l_attPenaltyDetail = null;
									if(l_lateMinutes > l_attPenaltyList.get(l_attPenaltyList.size() - 1).getToLateTime()){
										l_attPenaltyDetail = l_attPenaltyList.get(l_attPenaltyList.size() - 1);
									}else{
										for (int i = 1; i < l_attPenaltyList.size(); i++) {
											if(l_lateMinutes >= l_attPenaltyList.get(i).getFromLateTime() && 
													l_lateMinutes <= l_attPenaltyList.get(i).getToLateTime()){
												l_attPenaltyDetail = l_attPenaltyList.get(i);
												break;
											}
										}
									}
									if(l_attPenaltyDetail != null){
										l_wholeDayAbsence = (l_attPenaltyDetail.getAbsenceType() == 1);
										
										EmployeeAttendancePenalty l_empAttPenalty = new EmployeeAttendancePenalty();
										l_empAttPenalty.setDatetime_(l_dailyAttendance.getActualInDateTime());
										l_empAttPenalty.setAbsentPeriod(l_attPenaltyDetail.getAbsenceType());
										l_empAttPenalty.setCreatedDate(new Date());
										l_empAttPenalty.setEmployeeID(l_dailyAttendance.getEmployeeId());
										l_empAttPenalty.setPenaltyType(HRContants.AttendancePenaltyType.LATE_ARRIVAL);
										l_empAttPenalty.setMinutes(l_lateMinutes);
										l_empAttPenalty.setStatus(HRContants.AttendancePenaltyStatus.ACTIVE);
										entityManager.persist(l_empAttPenalty);
									}
								}
							}
							if(l_wholeDayAbsence)
								entityManager.persist(l_dailyAttendance);
						}else{
							DailyAttendance l_previousAttendance = null;
							try {
								l_previousAttendance = entityManager.createNamedQuery("DailyAttendance.getPreviousAttendance", DailyAttendance.class)
										.setParameter("inDateTime", l_log.getPunchDateTime())
										.setParameter("employeeId", l_emp.getEmployeeID())
										.getSingleResult();
							} catch (NoResultException innerEx) {}
							if(l_previousAttendance != null){
								if(l_previousAttendance.getActualOutDateTime() == null){
									l_previousAttendance.setActualOutDateTime(l_log.getPunchDateTime());
									entityManager.merge(l_previousAttendance);
								}else{
									if(l_previousAttendance.getActualOutDateTime().before(l_log.getPunchDateTime())){
										l_previousAttendance.setActualOutDateTime(l_log.getPunchDateTime());
										entityManager.merge(l_previousAttendance);
									}
								}
							}
						}
					}else{
						Overtime l_overtime = null;
						try {
							Date[] l_range = DateUtils.getRangeForDate(l_log.getPunchDateTime());
							l_overtime = entityManager.createNamedQuery("Overtime.findByDateNEmployeeId", Overtime.class)
								.setParameter("startDate", l_range[0])
								.setParameter("endDate", l_range[1])
								.setParameter("employeeId", l_emp.getEmployeeID())
								.getSingleResult();
						} catch (NoResultException e) {}
						if(l_overtime != null && l_dailyAttendance == null){
							Calendar l_otCalendar = Calendar.getInstance();
							l_otCalendar.setTime(l_overtime.getOtDate());
							
							l_dailyAttendance = new DailyAttendance();
							l_dailyAttendance.setActualInDateTime(l_log.getPunchDateTime());
							l_dailyAttendance.setBranchId(l_emp.getBranchId());
							l_dailyAttendance.setCreatedDate(new Date());
							l_dailyAttendance.setEmployeeId(l_emp.getEmployeeID());
							l_dailyAttendance.setId(KeyGenerator.generateKey(entityManager));
							l_dailyAttendance.setInDateTime(l_overtime.getOtDate());
							l_dailyAttendance.setAttendanceType(HRContants.DailyAttendanceType.OVERTIME);
							l_otCalendar.add(Calendar.MINUTE, l_overtime.getOtMinutes());
							l_dailyAttendance.setOutDateTime(l_otCalendar.getTime());
							
							entityManager.persist(l_dailyAttendance);
						}
					}
					l_log.setEmployeeNo(l_emp.getEmployeeNo());
					l_log.setCreatedDate(new Date());
					entityManager.persist(l_log);
				}
			
			}
		}
	}
	
	/***
	 * Creating attendances from logs which are sent from devices
	 * @param logs
	 * @throws Exception
	 */
	public void importLogs(List<AttendanceLog> logs)throws Exception{
		openConnection();
		try {
			beginTransaction();
			SystemProperty l_systemProperty = entityManager.find(SystemProperty.class, HRContants.AttendanceCalculationType.SYSTEM_PROPERTY_NAME);
			if(l_systemProperty != null){
				byte l_attCalType = Byte.parseByte(l_systemProperty.getValue());
				switch (l_attCalType) {
				case HRContants.AttendanceCalculationType.IN_OUT:
					createAttendnaceFromLog_1(logs);
					break;
				case HRContants.AttendanceCalculationType.NO_IN_OUT:
					createAttendanceFromLog_3(logs);
					break;
				}
			}
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	/***
	 * Creating attendance from log which are sent from devices
	 * @param logs
	 * @throws Exception
	 */
	public void importLog(AttendanceLog log)throws Exception{
		openConnection();
		try {
			List<AttendanceLog> l_logs = new ArrayList<AttendanceLog>();
			l_logs.add(log);
			beginTransaction();
			SystemProperty l_systemProperty = entityManager.find(SystemProperty.class, HRContants.AttendanceCalculationType.SYSTEM_PROPERTY_NAME);
			if(l_systemProperty != null){
				byte l_attCalType = Byte.parseByte(l_systemProperty.getValue());
				switch (l_attCalType) {
				case HRContants.AttendanceCalculationType.IN_OUT:
					createAttendnaceFromLog_1(l_logs);
					break;
				case HRContants.AttendanceCalculationType.NO_IN_OUT:
					createAttendanceFromLog_3(l_logs);
					break;
				}
			}
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	/***
	 * Creating attendance manually
	 * @param dailyAttendance
	 * @throws Exception
	 */
	public void createAttendance(DailyAttendance dailyAttendance)throws Exception{
		openConnection();
		try {
			beginTransaction();
			dailyAttendance.setCreatedDate(new Date());
			
			Employee l_emp = entityManager.find(Employee.class, dailyAttendance.getEmployeeId());
			dailyAttendance.setBranchId(l_emp.getBranchId());
			dailyAttendance.setEmployeeName(l_emp.getName());
			
			Calendar l_inCalendar = Calendar.getInstance();
			l_inCalendar.setTime(dailyAttendance.getActualInDateTime());
			
			AttendancePolicyDetail l_attendancePolicyDetail = null;
			try {
				l_attendancePolicyDetail = entityManager.createNamedQuery("AttendancePolicyDetail.getPolicyDetailByEmployeeIdNDay", AttendancePolicyDetail.class)
					.setParameter("employeeID", dailyAttendance.getEmployeeId())
					.setParameter("day", l_inCalendar.get(Calendar.DAY_OF_WEEK))
					.getSingleResult();
			} catch (NoResultException innerEx) {}
			
			if(l_attendancePolicyDetail == null){
				try {
					Date[] l_range = DateUtils.getRangeForDate(dailyAttendance.getActualInDateTime());
					entityManager.createNamedQuery("Overtime.findByDateNEmployeeId", Overtime.class)
						.setParameter("startDate", l_range[0])
						.setParameter("endDate", l_range[1])
						.setParameter("employeeId", dailyAttendance.getEmployeeId())
						.getSingleResult();
					dailyAttendance.setAttendanceType(HRContants.DailyAttendanceType.OVERTIME);
				} catch (NoResultException innerEx) {
					throw new DataNotFoundException("No over time on this day " + dailyAttendance.toString());
				}
			}else{
				boolean l_wholeDayAbsence = false;
				
				dailyAttendance.setAttendanceType(HRContants.DailyAttendanceType.NORMAL_ATTENDANCE);
				
				Calendar l_actualCalendar = Calendar.getInstance();
				l_actualCalendar.setTime(dailyAttendance.getActualInDateTime());
				l_actualCalendar.set(Calendar.HOUR_OF_DAY, 0);
				l_actualCalendar.set(Calendar.MINUTE, 0);
				l_actualCalendar.set(Calendar.SECOND, 0);
				l_actualCalendar.set(Calendar.MILLISECOND, 0);
				l_actualCalendar.add(Calendar.MINUTE, l_attendancePolicyDetail.getStartTime());
				
				dailyAttendance.setInDateTime(l_actualCalendar.getTime());
				l_actualCalendar.add(Calendar.MINUTE, l_attendancePolicyDetail.getWorkingHour());
				dailyAttendance.setOutDateTime(l_actualCalendar.getTime());
				
				if(dailyAttendance.getActualInDateTime().after(dailyAttendance.getInDateTime())){
					int l_lateMinutes = (int)(dailyAttendance.getActualInDateTime().getTime() - dailyAttendance.getInDateTime().getTime()) / 60000;
					List<AttendancePenaltyDetail> l_attPenaltyList = entityManager.createNamedQuery("AttendancePenaltyDetail.getPenaltyDetailsByEmployeeID", AttendancePenaltyDetail.class)
							.setParameter("employeeID", dailyAttendance.getEmployeeId())
							.getResultList();
					
					if(l_attPenaltyList.size() > 0){
						
						AttendancePenaltyDetail l_attPenaltyDetail = null;
						if(l_lateMinutes > l_attPenaltyList.get(l_attPenaltyList.size() - 1).getToLateTime()){
							l_attPenaltyDetail = l_attPenaltyList.get(l_attPenaltyList.size() - 1);
						}else{
							for (int i = 1; i < l_attPenaltyList.size(); i++) {
								if(l_lateMinutes >= l_attPenaltyList.get(i).getFromLateTime() && 
										l_lateMinutes <= l_attPenaltyList.get(i).getToLateTime()){
									l_attPenaltyDetail = l_attPenaltyList.get(i);
									break;
								}
							}
						}
						if(l_attPenaltyDetail != null){
							l_wholeDayAbsence = (l_attPenaltyDetail.getAbsenceType() == 1);
							
							EmployeeAttendancePenalty l_empAttPenalty = new EmployeeAttendancePenalty();
							l_empAttPenalty.setDatetime_(dailyAttendance.getActualInDateTime());
							l_empAttPenalty.setAbsentPeriod(l_attPenaltyDetail.getAbsenceType());
							l_empAttPenalty.setCreatedDate(new Date());
							l_empAttPenalty.setEmployeeID(dailyAttendance.getEmployeeId());
							l_empAttPenalty.setPenaltyType(HRContants.AttendancePenaltyType.LATE_ARRIVAL);
							l_empAttPenalty.setMinutes(l_lateMinutes);
							l_empAttPenalty.setStatus(HRContants.AttendancePenaltyStatus.ACTIVE);
							entityManager.persist(l_empAttPenalty);
						}
					}
				}
				if(dailyAttendance.getActualOutDateTime().before(dailyAttendance.getOutDateTime()) && !l_wholeDayAbsence){
					int l_earlyOutTime = (int)((dailyAttendance.getOutDateTime().getTime() - dailyAttendance.getActualOutDateTime().getTime()) / 60000);
					List<EarlyDeparturePenaltyDetail> l_attPenaltyList = entityManager.createNamedQuery("AttendancePenaltyDetail.getPenaltyDetailsByEmployeeID", EarlyDeparturePenaltyDetail.class)
							.setParameter("employeeID", dailyAttendance.getEmployeeId())
							.getResultList();
					
					if(l_attPenaltyList.size() > 0){
						EarlyDeparturePenaltyDetail l_attPenaltyDetail = null;
						if(l_earlyOutTime > l_attPenaltyList.get(l_attPenaltyList.size() - 1).getToEarlyTime()){
							l_attPenaltyDetail = l_attPenaltyList.get(l_attPenaltyList.size() - 1);
						}else{
							for (int i = 1; i < l_attPenaltyList.size(); i++) {
								if(l_earlyOutTime >= l_attPenaltyList.get(i).getFromEarlyTime() && 
										l_earlyOutTime <= l_attPenaltyList.get(i).getToEarlyTime()){
									l_attPenaltyDetail = l_attPenaltyList.get(i);
									break;
								}
							}
						}
						if(l_attPenaltyDetail != null){
							EmployeeAttendancePenalty l_empAttPenalty = new EmployeeAttendancePenalty();
							l_empAttPenalty.setDatetime_(dailyAttendance.getActualInDateTime());
							l_empAttPenalty.setAbsentPeriod(l_attPenaltyDetail.getAbsenceType());
							l_empAttPenalty.setCreatedDate(new Date());
							l_empAttPenalty.setEmployeeID(dailyAttendance.getEmployeeId());
							l_empAttPenalty.setPenaltyType(HRContants.AttendancePenaltyType.EARLY_DEPARTURE);
							l_empAttPenalty.setMinutes(l_earlyOutTime);
							l_empAttPenalty.setStatus(HRContants.AttendancePenaltyStatus.ACTIVE);
							entityManager.persist(l_empAttPenalty);
						}
					}
				}
				
				dailyAttendance.setCreatedDate(new Date());
				dailyAttendance.setId(KeyGenerator.generateKey(entityManager));
				
				entityManager.persist(dailyAttendance);
			}
			commitTransaction();
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public void updateAttendance(DailyAttendance dailyAttendance)throws Exception{
		openConnection();
		try {
			beginTransaction();
			Calendar l_inCalendar = Calendar.getInstance();
			l_inCalendar.setTime(dailyAttendance.getInDateTime());
			
			dailyAttendance.setModifiedDate(new Date());
			
			Calendar l_calendar = Calendar.getInstance();
			l_calendar.setTime(dailyAttendance.getActualInDateTime());
			
			try {
				EmployeeAttendancePenalty l_empAttPenalty = entityManager.createNamedQuery("EmployeeAttendancePenalty.findByEmployeeIDInDatePenaltyType", EmployeeAttendancePenalty.class)
						.setParameter("employeeID", dailyAttendance.getEmployeeId())
						.setParameter("datetime", dailyAttendance.getActualInDateTime(), TemporalType.DATE)
						.setParameter("penaltyType", HRContants.AttendancePenaltyType.EARLY_DEPARTURE)
						.getSingleResult();
				entityManager.remove(l_empAttPenalty);
			} catch (NoResultException innerEx) {}
			try {
				EmployeeAttendancePenalty l_empAttPenalty = entityManager.createNamedQuery("EmployeeAttendancePenalty.findByEmployeeIDInDatePenaltyType", EmployeeAttendancePenalty.class)
						.setParameter("employeeID", dailyAttendance.getEmployeeId())
						.setParameter("datetime", dailyAttendance.getActualInDateTime(), TemporalType.DATE)
						.setParameter("penaltyType", HRContants.AttendancePenaltyType.LATE_ARRIVAL)
						.getSingleResult();
				entityManager.remove(l_empAttPenalty);
			} catch (NoResultException innerEx) {}
			
			AttendancePolicyDetail l_attendancePolicyDetail = null;
			try {
				l_attendancePolicyDetail = entityManager.createNamedQuery("AttendancePolicyDetail.getPolicyDetailByEmployeeIdNDay", AttendancePolicyDetail.class)
					.setParameter("employeeID", dailyAttendance.getEmployeeId())
					.setParameter("day", l_calendar.get(Calendar.DAY_OF_WEEK))
					.getSingleResult();
			} catch (NoResultException innerEx) {}
			
			if(l_attendancePolicyDetail == null){
				
			}else{
				if(dailyAttendance.getActualInDateTime().after(dailyAttendance.getInDateTime())){
					int l_lateMinutes = (int)((dailyAttendance.getActualInDateTime().getTime() - dailyAttendance.getInDateTime().getTime()) / 60000);
					List<AttendancePenaltyDetail> l_attPenaltyList = entityManager.createNamedQuery("AttendancePenaltyDetail.getPenaltyDetailsByEmployeeID", AttendancePenaltyDetail.class)
							.setParameter("employeeID", dailyAttendance.getEmployeeId())
							.getResultList();
					AttendancePenaltyDetail l_attPenaltyDetail = null;
					if(l_lateMinutes > l_attPenaltyList.get(l_attPenaltyList.size() - 1).getToLateTime()){
						l_attPenaltyDetail = l_attPenaltyList.get(l_attPenaltyList.size() - 1);
					}else{
						for (int i = 1; i < l_attPenaltyList.size(); i++) {
							if(l_lateMinutes >= l_attPenaltyList.get(i).getFromLateTime() && 
									l_lateMinutes <= l_attPenaltyList.get(i).getToLateTime()){
								l_attPenaltyDetail = l_attPenaltyList.get(i);
								break;
							}
						}
					}
					if(l_attPenaltyDetail != null){
						EmployeeAttendancePenalty l_empAttPenalty = new EmployeeAttendancePenalty();
						l_empAttPenalty.setDatetime_(dailyAttendance.getActualInDateTime());
						l_empAttPenalty.setAbsentPeriod(l_attPenaltyDetail.getAbsenceType());
						l_empAttPenalty.setCreatedDate(new Date());
						l_empAttPenalty.setEmployeeID(dailyAttendance.getEmployeeId());
						l_empAttPenalty.setPenaltyType(HRContants.AttendancePenaltyType.LATE_ARRIVAL);
						l_empAttPenalty.setMinutes(l_lateMinutes);
						l_empAttPenalty.setStatus(HRContants.AttendancePenaltyStatus.ACTIVE);
						entityManager.persist(l_empAttPenalty);
					}
				}
				if(dailyAttendance.getActualOutDateTime().before(dailyAttendance.getOutDateTime())){
					int l_earlyOutTime = (int)((dailyAttendance.getOutDateTime().getTime() - dailyAttendance.getActualOutDateTime().getTime()) / 60000);
					List<EarlyDeparturePenaltyDetail> l_attPenaltyList = entityManager.createNamedQuery("AttendancePenaltyDetail.getPenaltyDetailsByEmployeeID", EarlyDeparturePenaltyDetail.class)
							.setParameter("employeeID", dailyAttendance.getEmployeeId())
							.getResultList();
					
					if(l_attPenaltyList.size() > 0){
						EarlyDeparturePenaltyDetail l_attPenaltyDetail = null;
						if(l_earlyOutTime > l_attPenaltyList.get(l_attPenaltyList.size() - 1).getFromEarlyTime()){
							l_attPenaltyDetail = l_attPenaltyList.get(l_attPenaltyList.size() - 1);
						}else{
							for (int i = 1; i < l_attPenaltyList.size(); i++) {
								if(l_earlyOutTime >= l_attPenaltyList.get(i).getFromEarlyTime() && 
										l_earlyOutTime <= l_attPenaltyList.get(i).getFromEarlyTime()){
									l_attPenaltyDetail = l_attPenaltyList.get(i);
									break;
								}
							}
						}
						if(l_attPenaltyDetail != null){
							EmployeeAttendancePenalty l_empAttPenalty = new EmployeeAttendancePenalty();
							l_empAttPenalty.setDatetime_(dailyAttendance.getActualInDateTime());
							l_empAttPenalty.setAbsentPeriod(l_attPenaltyDetail.getAbsenceType());
							l_empAttPenalty.setCreatedDate(new Date());
							l_empAttPenalty.setEmployeeID(dailyAttendance.getEmployeeId());
							l_empAttPenalty.setPenaltyType(HRContants.AttendancePenaltyType.EARLY_DEPARTURE);
							l_empAttPenalty.setMinutes(l_earlyOutTime);
							l_empAttPenalty.setStatus(HRContants.AttendancePenaltyStatus.ACTIVE);
							entityManager.persist(l_empAttPenalty);
						}
					}
				}
			}
			
			entityManager.merge(dailyAttendance);
			
			DailyAttendanceLog l_log = new DailyAttendanceLog();
			l_log.setActualInDateTime(dailyAttendance.getActualInDateTime());
			l_log.setActualOutDateTime(dailyAttendance.getActualOutDateTime());
			l_log.setBranchId(dailyAttendance.getBranchId());
			l_log.setCreatedBy(dailyAttendance.getCreatedBy());
			l_log.setCreatedDate(dailyAttendance.getCreatedDate());
			l_log.setEmployeeId(dailyAttendance.getEmployeeId());
			l_log.setId(KeyGenerator.generateKey(entityManager));
			l_log.setInDateTime(dailyAttendance.getInDateTime());
			l_log.setModifiedby(dailyAttendance.getModifiedby());
			l_log.setModifiedDate(dailyAttendance.getModifiedDate());
			l_log.setOutDateTime(dailyAttendance.getOutDateTime());
			l_log.setRemark(dailyAttendance.getRemark());
			l_log.setAttendanceType(dailyAttendance.getAttendanceType());
			entityManager.persist(l_log);
			
			commitTransaction();
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public void deleteAttendance(long id)throws Exception{
		openConnection();
		try {
			beginTransaction();
			DailyAttendance l_dailyAttendance = entityManager.find(DailyAttendance.class, id);
			l_dailyAttendance.setId(id);
			try {
				EmployeeAttendancePenalty l_empAttPenalty = entityManager.createNamedQuery("EmployeeAttendancePenalty.findByEmployeeIDInDatePenaltyType", EmployeeAttendancePenalty.class)
						.setParameter("employeeID", l_dailyAttendance.getEmployeeId())
						.setParameter("datetime", l_dailyAttendance.getActualInDateTime(), TemporalType.DATE)
						.setParameter("penaltyType", HRContants.AttendancePenaltyType.LATE_ARRIVAL)
						.getSingleResult();
				entityManager.remove(l_empAttPenalty);
			} catch (NoResultException innerEx) {}
			try {
				EmployeeAttendancePenalty l_empAttPenalty = entityManager.createNamedQuery("EmployeeAttendancePenalty.findByEmployeeIDInDatePenaltyType", EmployeeAttendancePenalty.class)
						.setParameter("employeeID", l_dailyAttendance.getEmployeeId())
						.setParameter("datetime", l_dailyAttendance.getActualInDateTime(), TemporalType.DATE)
						.setParameter("penaltyType", HRContants.AttendancePenaltyType.EARLY_DEPARTURE)
						.getSingleResult();
				entityManager.remove(l_empAttPenalty);
			} catch (NoResultException innerEx) {}
			entityManager.remove(l_dailyAttendance);
			commitTransaction();;
		} catch (Exception e) {
			rollbackTransaction();
			throw e;
		} finally {
			closeConnection();
		}
	}
	
	public DailyAttendanceDataList findDaliyAttendances(DailyAttendanceCriteria cri)throws Exception{
		openConnection();
		DailyAttendanceDataList l_dataList = new DailyAttendanceDataList();
		try {
			
			String l_cri = "";
			String l_jpql = "select da, e.name from DailyAttendance da, Employee e where da.employeeId = e.employeeID" + l_cri;
			List<Object[]> l_results = entityManager.createQuery(l_jpql, Object[].class)
					.setFirstResult(cri.getIndex())
                    .setMaxResults(cri.getSize())
					.getResultList();
			List<DailyAttendance> l_daList = new ArrayList<DailyAttendance>();
			for (Object[] l_result : l_results) {
				DailyAttendance l_dailyAttendance = (DailyAttendance)l_result[0];
				l_dailyAttendance.setEmployeeName(l_result[1].toString());
				l_daList.add(l_dailyAttendance);
			}
			l_dataList.setEntityList(l_daList);
			if(cri.getIndex() == 0){
				l_jpql = "select count(da.id) from DailyAttendance da, Employee e where da.employeeId = e.employeeID" + l_cri;
				l_dataList.setTotalRecord(entityManager.createQuery(l_jpql, Long.class).getSingleResult());
			}
		} catch (Exception e) {
			throw e;
		} finally {
			closeConnection();
		}
		return l_dataList;
	}
}
